#include <iostream>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include <stdio.h>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
#include <vector>
#include <list>
#include <filesystem>

#include "fmt/core.h"
//#include "fmt/src/format.cc"  // 定义了 FMT_HEADER_ONLY 才需要

#define ShaderExtFilter ".comp"
#define IsCompileOk 0

using  stddir = std::experimental::filesystem::v1::directory_entry;
using  stddir_it = std::experimental::filesystem::v1::directory_iterator;
using  stdpath = std::experimental::filesystem::v1::path;


int main()
{
    using namespace std;

    //  查换计算着色器代码文件
    list<string> filesName;
    for (auto & dir : stddir_it(".")) {
        string fileName = dir.path().filename().string();
//        cout<< "fileName = " << fileName << endl;
        string extensionName = dir.path().extension().string();
        transform(extensionName.begin(), extensionName.end(), // src
                  extensionName.begin(),  //dst
                  [](char c){ return tolower(c); }
        );

        if(extensionName == ShaderExtFilter){
            filesName.push_back(fileName);
        }
//        cout<< "extensionName = " << extensionName << endl;
    }

    filesName.sort();
    cout<< "----------- gen_spriv_src -------------" <<endl;
    cout<< "----------- files name -------------" <<endl;
    for(const auto & fn : filesName){
        cout<< fn << endl;
    }
    cout<< "------------------------" <<endl;

    //  编译glsl着色器为spirv代码文件
    list<string> outsprivFilesName{
        filesName.begin(),
                filesName.end()
    };

    transform(outsprivFilesName.begin(), outsprivFilesName.end(),
              outsprivFilesName.begin(),
              [](string name){
        stdpath path(name.begin(),name.end());
        string ret = path.stem().string();
//        cout<< "path stripped of its extension = " << ret << endl;
        return ret;
    });

    for(auto & s : outsprivFilesName){
        transform(s.begin(),s.end(),
                  s.begin(),
                  [](char c){
            if(c == ' ') return '_';
            else if(c == '-') return '_';
            else return c;
        });
    }

    int count = static_cast<int>(filesName.size());
    if(count != outsprivFilesName.size()){
        cerr << "file count neq!" << endl;
    }

    int compiledokcnt = 0;
    vector<string> srcname{filesName.begin(),filesName.end()};
    vector<string> dstname{outsprivFilesName.begin(),outsprivFilesName.end()};
    for (int i = 0; i < count; ++i) {
        std::ostringstream cmd;
        cmd << "glslangValidator -V \"";
        cmd << srcname[i] << "\" -o \"";
        cmd << dstname[i] << ".spv\"";
//        cout<< cmd.str() << endl;
        if(system(cmd.str().c_str()) != IsCompileOk){
            cerr << srcname[i] << " compile failed! " << endl;
        }else{
            compiledokcnt++;
            cout << srcname[i] << " compile success! " << endl;
        }
    }

    if(compiledokcnt != count || count < 1){
        cerr << " no file found or compile file failed!" << endl;
        return 1;
    }

    //  读取编码后的spirv文件
    vector<vector<uint32_t>> spv_datas;
    spv_datas.resize(compiledokcnt);
    for (int i = 0; i < compiledokcnt; ++i) {
        string fn = dstname[i] + ".spv";
        std::ifstream fileStream(fn, std::ios::binary);
        std::vector<char> buffer((std::istreambuf_iterator<char>(fileStream)),
                                 std::istreambuf_iterator<char>()
                                 );
        if(buffer.size() < 1 || buffer.size() % 4 != 0){
            cerr << "invalid spirv file : " << fn << endl;
            return 1;
        }
        spv_datas[i].insert(spv_datas[i].begin(),
                            reinterpret_cast<uint32_t*>(buffer.data()),
                            reinterpret_cast<uint32_t*>(buffer.data() + buffer.size())
                            );
    }

    //  生成h头文件和cpp文件
    {
        std::ofstream headerfs("kpextSpvData.h", std::ios::trunc | std::ios::out);
        std::ofstream cppfs("kpextSpvData.cpp", std::ios::trunc | std::ios::out);
        cppfs << "#include \"kpextSpvData.h\"\n\n" << endl;
        cppfs <<  "namespace spirv{\n\n" << endl;

        headerfs << "#ifndef KPEXT_SPV_DATA_H" << endl;
        headerfs << "#define KPEXT_SPV_DATA_H\n" << endl;
        headerfs <<  "namespace spirv{ \n\n" << endl;

        for (int i = 0; i < compiledokcnt; ++i) {
            headerfs << "extern const unsigned int ";
            headerfs << dstname[i];
            headerfs << fmt::format("[{}];\n", spv_datas[i].size()) << endl;

            cppfs << "const unsigned int ";
            cppfs << dstname[i];
            cppfs << fmt::format("[{}] = \n {{", spv_datas[i].size()) << endl;

            stringstream ss;
            int c = 0;
            for(auto & tempui : spv_datas[i]){
                ss << fmt::format("{:#010x}, ", tempui);
                c++;
                if(c == 8) ss << std::endl;
                c = c % 8;
            }
            cppfs << ss.str();
            cppfs << "};\n\n";
        }

        cppfs << "\n} // namespace spirv" << endl;

        headerfs <<  "} // namespace spirv\n" << endl;
        headerfs << "#endif //KPEXT_SPV_DATA_H" << endl;
    }


//    system("pause");
    return 0;
}
